Here I experiment with combining flow fields with images (dithering).

Load packages:

library(ggnewscale)
library(imager)
library(sf)
library(tidyverse)

Process image - snowscape

Read the image using imager::load.image():

#melville <- image_read("melville_lg.jpg")
snowscape <- load.image("snowscape.jpg")

Check the size of the image:

summary(snowscape)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.1686  0.4118  0.4195  0.5843  1.0000 

Convert image to data frame (still working with {imager}):

snowscape_df <- snowscape %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

snowscape_df_grayscale <- snowscape %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

snowscape_df <- snowscape_df %>%
  left_join(snowscape_df_grayscale,
            by = c("x", "y"))

Retrieve every nth row/column from image:

nx <- 10
ny <- 20

snowscape_sampled <- snowscape_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Plot:

snowscape_sampled %>%
  ggplot(aes(x, 
             y, 
             color = color)) + 
  geom_point(aes(size = value)) + 
  scale_color_identity() +
  scale_y_reverse() + 
  scale_size(range = c(1, 6)) +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("snowscape-circles.png")
Saving 7.29 x 4.51 in image

Plot with different shape:

snowscape_sampled %>%
  ggplot(aes(x, 
             y, 
             color = color)) + 
  geom_point(aes(size = value),
             shape = 17) +
  geom_point(size = 0.1,
             color = "black",
             shape = 20) +
  scale_color_identity() +
  scale_size(range = c(1, 5)) +
  scale_y_reverse() + 
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("snowscape-dotted-diamonds.png")
Saving 7.29 x 4.51 in image

Retrieve every nth row/column from image:

nx <- 10
ny <- 20

snowscape_sampled <- snowscape_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Define length of segments:

l <- 15 # Experiment with the length

Create and plot the flow field:


n_min <- min(snowscape_sampled$x)
n_max <- max(snowscape_sampled$x)

# Offsets
x_o <- -max(snowscape_sampled$x)/2 # Experiment with the sign and the fraction
y_o <- 0 #max(snowscape_sampled$y)

df <- snowscape_sampled %>%
  mutate(angle = 1 * pi/6 * (1 -(x - x_o)/(n_max - n_min)),
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend,
                   size = value,
                   color = color)) +
  scale_size(range = c(1, 8)) + # Experiment with the range
  scale_color_identity() +
  scale_y_reverse() +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("snowscape-flow-field.png")
Saving 7.29 x 4.51 in image

Process image - Meghan

Read the image using imager::load.image():

meghan <- load.image("meghan.jpg")

Check the size of the image:

summary(meghan)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.2980  0.6510  0.5623  0.8118  1.0000 

Convert image to data frame (still working with {imager}):

meghan_df <- meghan %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

meghan_df_grayscale <- meghan %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

meghan_df <- meghan_df %>%
  left_join(meghan_df_grayscale,
            by = c("x", "y"))

Crop image:

meghan_df <- meghan_df %>%
  filter(x <= 1250, y <= 1250)

Retrieve every nth row/column from image:

nx <- 20
ny <- 20

meghan_sampled <- meghan_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Plot:

meghan_sampled %>%
  ggplot() + 
  geom_point(aes(x, 
                 y,
                 color = value),
             shape = 15,
             size = 6) +
  scale_color_fermenter(palette = "greens") +
  new_scale_color() +
  geom_point(aes(x, 
                 y, 
                 color = color,
                 size = value)) + 
  scale_color_identity() +
  scale_y_reverse() + 
  scale_size(range = c(0.1, 3)) +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")
Warning in pal_name(palette, type) : Unknown palette greens
ggsave("meghan-circles.png",
       height = 8,
       width = 8,
       units = "in")

Plot with different shape:

meghan_sampled %>%
  ggplot() + 
  geom_point(aes(x,
                 y),
             color = "black",
             shape = 15,
             size = 6) +
  geom_point(aes(x, 
                 y,
                 color = color,
                 size = value),
             shape = 17) +
  # geom_point(aes(x, 
  #            y),
  #            size = 0.1,
  #            color = "black",
  #            shape = 20) +
  scale_color_identity() +
  scale_size(range = c(1, 4)) +
  scale_y_reverse() + 
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("meghan-diamonds.png",
       height = 8,
       width = 8,
       units = "in")

Retrieve every nth row/column from image:

nx <- 25
ny <- 25

meghan_sampled <- meghan_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Define length of segments:

l <- 25 # Experiment with the length

Create and plot the flow field:

n_min <- min(meghan_sampled$x)
n_max <- max(meghan_sampled$x)

# Offsets
x_o <- 0 #max(meghan_sampled$x)/2 # Experiment with the sign and the fraction
y_o <- 0 #max(meghan_sampled$y)
# Initial rotation
init_r <- 1/8


df <- meghan_sampled %>%
  mutate(angle = ((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi + init_r * pi,
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_point(aes(x,
                 y, 
                 color = value),
             #color = "black",
             shape = 15,
             size = 6) +
  scale_color_fermenter(palette = "Greys") +
  new_scale_color() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend,
                   size = value,
                   color = color)) +
  scale_size(range = c(0.5, 3)) + # Experiment with the range
  scale_color_identity() +
  scale_y_reverse() +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("meghan-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

Process image - Mariana

Read the image using imager::load.image():

mariana <- load.image("mariana-gato.jpg")

Check the size of the image:

summary(mariana)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.2784  0.4353  0.4709  0.6784  1.0000 

Convert image to data frame (still working with {imager}):

mariana_df <- mariana %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

mariana_df_grayscale <- mariana %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

mariana_df <- mariana_df %>%
  left_join(mariana_df_grayscale,
            by = c("x", "y"))

Crop image:

#mariana_df <- mariana_df %>%
#  filter(x <= 1250, y <= 1250)

Retrieve every nth row/column from image:

nx <- 30
ny <- 30

mariana_sampled <- mariana_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Plot:

mariana_sampled %>%
  ggplot() + 
  geom_point(aes(x, 
                 y,
                 color = value),
             shape = 15,
             size = 6) +
  scale_color_fermenter(palette = "Greys") +
  new_scale_color() +
  geom_point(aes(x, 
                 y, 
                 color = color,
                 size = value)) + 
  scale_color_identity() +
  scale_y_reverse() + 
  scale_size(range = c(0.1, 3)) +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")


# ggsave("meghan-circles.png",
#        height = 8,
#        width = 8,
#        units = "in")

Plot with different shape:

mariana_sampled %>%
  ggplot() + 
  geom_point(aes(x,
                 y),
             color = "black",
             shape = 15,
             size = 6) +
  geom_point(aes(x, 
                 y,
                 color = color,
                 size = value),
             shape = 17) +
  # geom_point(aes(x, 
  #            y),
  #            size = 0.1,
  #            color = "black",
  #            shape = 20) +
  scale_color_identity() +
  scale_size(range = c(1, 4)) +
  scale_y_reverse() + 
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")


# ggsave("meghan-diamonds.png",
#        height = 8,
#        width = 8,
#        units = "in")

Retrieve every nth row/column from image:

nx <- 25
ny <- 25

mariana_sampled <- mariana_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Define length of segments:

l <- 25 # Experiment with the length

Create and plot the flow field:

n_min <- min(meghan_sampled$x)
n_max <- max(meghan_sampled$x)

# Offsets
x_o <- 0 #max(meghan_sampled$x)/2 # Experiment with the sign and the fraction
y_o <- 0 #max(meghan_sampled$y)
# Initial rotation
init_r <- 1/8


df <- mariana_sampled %>%
  mutate(angle = ((x - x_o) * (y - y_o)) /(n_max - n_min)^2 * 2 * pi + init_r * pi,
         xend = (x + l * cos(angle)),
         yend = (y + l * sin(angle)))

df %>%
  ggplot() +
  geom_point(aes(x,
                 y, 
                 color = value),
             color = "black",
             shape = 15,
             size = 6) +
  scale_color_distiller(palette = "Greys") +
  new_scale_color() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend,
                   size = value,
                   color = color)) +
  scale_size(range = c(0.5, 3)) + # Experiment with the range
  scale_color_identity() +
  scale_y_reverse() +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("mariana-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

Process image - Oregon

Read the image using imager::load.image():

oregon <- load.image("oregon.jpg")

Check the size of the image:

summary(oregon)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.3137  0.4627  0.4681  0.6157  1.0000 

Convert image to data frame (still working with {imager}):

oregon_df <- oregon %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

oregon_df_grayscale <- oregon %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

oregon_df <- oregon_df %>%
  left_join(oregon_df_grayscale,
            by = c("x", "y"))

Retrieve every nth row/column from image:

nx <- 10
ny <- 10

oregon_sampled <- oregon_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Create and plot the flow field:

# Offsets
x_o <- 560
y_o <- 140


df <- oregon_sampled %>%
  #length of segments depends on position, and the angle is constant from x_o, y_o
  mutate(xend = (x + (x - x_o)/30 * pi/4),
         yend = (y + (y - y_o)/30 * pi/4))

df %>%
  ggplot() +
  geom_point(aes(x,
                 y,
                 color = value),
             shape = 15,
             size = 6) +
  scale_color_distiller(palette = "Greys") +
  new_scale_color() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend,
                   size = value,
                   color = color)) +
  scale_size(range = c(0.1, 2)) + # Experiment with the range
  scale_color_identity() +
  scale_y_reverse() +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("oregon-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

Waldport

See https://twitter.com/mark_lawler/status/1490467641676337154/photo/2 for this image:

Read the image using imager::load.image():

waldport <- load.image("waldport.jpg")

Check the size of the image:

summary(waldport)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.2235  0.4235  0.4616  0.6902  1.0000 

Convert image to data frame (still working with {imager}):

waldport_df <- waldport %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

waldport_df_grayscale <- waldport %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

waldport_df <- waldport_df %>%
  left_join(waldport_df_grayscale,
            by = c("x", "y"))

Retrieve every nth row/column from image:

nx <- 20
ny <- 20

waldport_sampled <- waldport_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Create and plot the flow field:

# Offsets
x_o <- -1000
y_o <- 770


df <- waldport_sampled %>%
  #length of segments depends on position, and the angle is constant from x_o, y_o
  mutate(xend = (x + (x - x_o)/20 * pi/8),
         yend = (y + (y - y_o)/20 * pi/8))

df %>%
  ggplot() +
  geom_point(aes(x,
                 y,
                 color = rev(y)),
             shape = 15,
             size = 6) +
  scale_color_gradientn(colors = MexBrewer::mex.brewer("Atentado")) +
  new_scale_color() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend,
                   size = value,
                   color = color)) +
  scale_size(range = c(0.01, 0.75)) + # Experiment with the range
  scale_color_identity() +
  scale_y_reverse() +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("waldport-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

One more of another beach! Check this: https://twitter.com/GiuIianoDMedici/status/1490715140051947521

Read the image using imager::load.image():

beach <- load.image("beach.jpg")

Check the size of the image:

beach
Image. Width: 899 pix Height: 1199 pix Depth: 1 Colour channels: 3 

Convert image to data frame (still working with {imager}):

beach_df <- beach %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

beach_df_grayscale <- beach %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

beach_df <- beach_df %>%
  left_join(beach_df_grayscale,
            by = c("x", "y"))

Retrieve every nth row/column from image:

nx <- 10
ny <- 10

beach_sampled <- beach_df %>%
  filter(x %% nx == 1,
         y %% ny == 1) 

Create and plot the flow field:

# Offsets
x_o <- 200
y_o <- 480


df <- beach_sampled %>%
  #length of segments depends on position, and the angle is constant from x_o, y_o
  mutate(xend = (x + (x - x_o)/20 * pi/4),
         yend = (y + (y - y_o)/20 * pi/4))

df %>%
  ggplot() +
  geom_point(aes(x,
                 y,
                 color = rev(y)),
             shape = 15,
             size = 6) +
  scale_color_gradientn(colors = MexBrewer::mex.brewer("Atentado")) +
  new_scale_color() +
  geom_segment(aes(x = x,
                   y = y,
                   xend = xend,
                   yend = yend,
                   size = sqrt((x - x_o)^2 + (y - y_o)^2),
                   color = color)) +
  scale_size(range = c(0.5, 1.5)) + # Experiment with the range
  scale_color_identity() +
  scale_y_reverse() +
  coord_equal(expand = FALSE) +
  theme_void() +
  theme(legend.position = "none")

ggsave("beach-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

Flower 1 and spirals

Read the image using imager::load.image():

flower <- load.image("flower.jpg")

Check the size of the image:

flower
Image. Width: 720 pix Height: 960 pix Depth: 1 Colour channels: 3 

Convert image to data frame (still working with {imager}):

flower_df <- flower %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

flower_df_grayscale <- flower %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

flower_df <- flower_df %>%
  left_join(flower_df_grayscale,
            by = c("x", "y"))

Convert the image to sf to use sf functions to find nearest features to borrow colors:

flower_sf <- flower_df %>%
    st_as_sf(coords = c("x", "y"))

Golden spiral:

#points <- 500

# Defining the Golden Angle
angle <- pi * (3 - sqrt(5))

#t_o <- seq(1, 10, 1) 

gs <- data.frame(t_o = seq(1, 750, 0.1) * angle) %>%
  mutate(x_o = 300 + t_o * sin(t_o),
         y_o = 300 + t_o * cos(t_o),
         x_end = x_o + 20 * sin(t_o),
         y_end = y_o + 20 * cos(t_o))

Convert to sf to borrow the colors from the image:

gs <- gs %>%
  mutate(x = x_o,
         y = y_o) %>%
  st_as_sf(coords = c("x", "y"))

Find the nearest feature in the original image to borrow the colors:

flower_colors <- flower_df[gs %>%
                                   st_nearest_feature(flower_sf),]

Bind the colors to the packed circles:

gs <- gs %>%
  mutate(color = flower_colors$color,
         value = flower_colors$value)

Plot:

# Make a scatter plot of points in a spiral
ggplot() +
  geom_point(data= flower_df,
             aes(x,
                 y,
                 color = value),
             #color = "black",
             shape = 15,
             size = 6) +
  scale_color_gradientn(colors = MexBrewer::mex.brewer("Atentado")) +
  #scale_color_distiller(palette = "Blues") +
  new_scale_color() +
  geom_segment(data = gs %>%
                 filter(x_o > 10 & x_o < 710,
                        y_o > 10 & y_o < 950),
               aes(x = x_o,
                   xend = x_end,
                   y = y_o,
                   yend = y_end,
                   size = t_o,
                   color = color)) +
  scale_y_reverse() +
  scale_color_identity() +
  scale_size(range = c(0.1, 3)) +
  coord_equal() +
  theme_void() +
  theme(legend.position = "none")

ggsave("flower-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

Flower 2 and spirals

Read the image using imager::load.image():

flower <- load.image("flower-2.jpg")

Check the size of the image:

flower
Image. Width: 720 pix Height: 960 pix Depth: 1 Colour channels: 3 

Convert image to data frame (still working with {imager}):

flower_df <- flower %>%
  as.data.frame(wide="c") %>% 
  mutate(color = rgb(c.1,
                     c.2,
                     c.3))

Convert to grayscale and to data frame:

flower_df_grayscale <- flower %>%
  grayscale() %>% 
  as.data.frame()

Join data frames with color and grayscale values:

flower_df <- flower_df %>%
  left_join(flower_df_grayscale,
            by = c("x", "y"))

Convert the image to sf to use sf functions to find nearest features to borrow colors:

flower_sf <- flower_df %>%
    st_as_sf(coords = c("x", "y"))

Golden spiral:

#points <- 500

# Defining the Golden Angle
angle <- pi * (3 - sqrt(5))

#t_o <- seq(1, 10, 1) 

gs <- data.frame(t_o = seq(1, 750, 0.1) * angle) %>%
  mutate(x_o = 160 + t_o * sin(t_o),
         y_o = 310 + t_o * cos(t_o),
         x_end = x_o + 20 * sin(t_o),
         y_end = y_o + 20 * cos(t_o))

Convert to sf to borrow the colors from the image:

gs <- gs %>%
  mutate(x = x_o,
         y = y_o) %>%
  st_as_sf(coords = c("x", "y"))

Find the nearest feature in the original image to borrow the colors:

flower_colors <- flower_df[gs %>%
                                   st_nearest_feature(flower_sf),]

Bind the colors to the spiral:

gs <- gs %>%
  mutate(color = flower_colors$color,
         value = flower_colors$value)

Plot:

# Make a scatter plot of points in a spiral
ggplot() +
  # geom_point(data= flower_df,
  #            aes(x,
  #                y,
  #                color = value),
  #            #color = "black",
  #            shape = 15,
  #            size = 6) +
  # scale_color_gradientn(colors = MexBrewer::mex.brewer("Atentado")) +
  # scale_color_distiller(palette = "Blues") +
  # new_scale_color() +
  geom_segment(data = gs %>%
                 filter(x_o > 10 & x_o < 710,
                        y_o > 10 & y_o < 950),
               aes(x = x_o,
                   xend = x_end,
                   y = y_o,
                   yend = y_end,
                   size = t_o,
                   color = color)) +
  scale_y_reverse() +
  scale_color_identity() +
  scale_size(range = c(0.1, 3)) +
  coord_equal() +
  theme_void() +
  theme(legend.position = "none")

ggsave("flower-2-flow-field.png",
       height = 8,
       width = 8,
       units = "in")

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSGVyZSBJIGV4cGVyaW1lbnQgd2l0aCBjb21iaW5pbmcgZmxvdyBmaWVsZHMgd2l0aCBpbWFnZXMgKGRpdGhlcmluZykuCgpMb2FkIHBhY2thZ2VzOgpgYGB7cn0KbGlicmFyeShnZ25ld3NjYWxlKQpsaWJyYXJ5KGltYWdlcikKbGlicmFyeShzZikKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgUHJvY2VzcyBpbWFnZSAtIHNub3dzY2FwZQoKUmVhZCB0aGUgaW1hZ2UgdXNpbmcgYGltYWdlcjo6bG9hZC5pbWFnZSgpYDoKYGBge3J9CiNtZWx2aWxsZSA8LSBpbWFnZV9yZWFkKCJtZWx2aWxsZV9sZy5qcGciKQpzbm93c2NhcGUgPC0gbG9hZC5pbWFnZSgic25vd3NjYXBlLmpwZyIpCmBgYAoKQ2hlY2sgdGhlIHNpemUgb2YgdGhlIGltYWdlOiAKYGBge3J9CnN1bW1hcnkoc25vd3NjYXBlKQpgYGAKCkNvbnZlcnQgaW1hZ2UgdG8gZGF0YSBmcmFtZSAoc3RpbGwgd29ya2luZyB3aXRoIHtpbWFnZXJ9KToKYGBge3J9CnNub3dzY2FwZV9kZiA8LSBzbm93c2NhcGUgJT4lCiAgYXMuZGF0YS5mcmFtZSh3aWRlPSJjIikgJT4lIAogIG11dGF0ZShjb2xvciA9IHJnYihjLjEsCiAgICAgICAgICAgICAgICAgICAgIGMuMiwKICAgICAgICAgICAgICAgICAgICAgYy4zKSkKYGBgCgpDb252ZXJ0IHRvIGdyYXlzY2FsZSBhbmQgdG8gZGF0YSBmcmFtZToKYGBge3J9CnNub3dzY2FwZV9kZl9ncmF5c2NhbGUgPC0gc25vd3NjYXBlICU+JQogIGdyYXlzY2FsZSgpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKYGBgCgpKb2luIGRhdGEgZnJhbWVzIHdpdGggY29sb3IgYW5kIGdyYXlzY2FsZSB2YWx1ZXM6CmBgYHtyfQpzbm93c2NhcGVfZGYgPC0gc25vd3NjYXBlX2RmICU+JQogIGxlZnRfam9pbihzbm93c2NhcGVfZGZfZ3JheXNjYWxlLAogICAgICAgICAgICBieSA9IGMoIngiLCAieSIpKQpgYGAKClJldHJpZXZlIGV2ZXJ5IG50aCByb3cvY29sdW1uIGZyb20gaW1hZ2U6CmBgYHtyfQpueCA8LSAxMApueSA8LSAyMAoKc25vd3NjYXBlX3NhbXBsZWQgPC0gc25vd3NjYXBlX2RmICU+JQogIGZpbHRlcih4ICUlIG54ID09IDEsCiAgICAgICAgIHkgJSUgbnkgPT0gMSkgCmBgYAoKUGxvdDoKYGBge3J9CnNub3dzY2FwZV9zYW1wbGVkICU+JQogIGdncGxvdChhZXMoeCwgCiAgICAgICAgICAgICB5LCAKICAgICAgICAgICAgIGNvbG9yID0gY29sb3IpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemUgPSB2YWx1ZSkpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKyAKICBzY2FsZV9zaXplKHJhbmdlID0gYygxLCA2KSkgKwogIGNvb3JkX2VxdWFsKGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpnZ3NhdmUoInNub3dzY2FwZS1jaXJjbGVzLnBuZyIpCmBgYAoKUGxvdCB3aXRoIGRpZmZlcmVudCBzaGFwZToKYGBge3J9CnNub3dzY2FwZV9zYW1wbGVkICU+JQogIGdncGxvdChhZXMoeCwgCiAgICAgICAgICAgICB5LCAKICAgICAgICAgICAgIGNvbG9yID0gY29sb3IpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemUgPSB2YWx1ZSksCiAgICAgICAgICAgICBzaGFwZSA9IDE3KSArCiAgZ2VvbV9wb2ludChzaXplID0gMC4xLAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgc2hhcGUgPSAyMCkgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsIDUpKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKyAKICBjb29yZF9lcXVhbChleHBhbmQgPSBGQUxTRSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKZ2dzYXZlKCJzbm93c2NhcGUtZG90dGVkLWRpYW1vbmRzLnBuZyIpCmBgYAoKClJldHJpZXZlIGV2ZXJ5IG50aCByb3cvY29sdW1uIGZyb20gaW1hZ2U6CmBgYHtyfQpueCA8LSAxMApueSA8LSAyMAoKc25vd3NjYXBlX3NhbXBsZWQgPC0gc25vd3NjYXBlX2RmICU+JQogIGZpbHRlcih4ICUlIG54ID09IDEsCiAgICAgICAgIHkgJSUgbnkgPT0gMSkgCmBgYAoKRGVmaW5lIGxlbmd0aCBvZiBzZWdtZW50czoKYGBge3J9CmwgPC0gMTUgIyBFeHBlcmltZW50IHdpdGggdGhlIGxlbmd0aApgYGAKCkNyZWF0ZSBhbmQgcGxvdCB0aGUgZmxvdyBmaWVsZDoKYGBge3J9CgpuX21pbiA8LSBtaW4oc25vd3NjYXBlX3NhbXBsZWQkeCkKbl9tYXggPC0gbWF4KHNub3dzY2FwZV9zYW1wbGVkJHgpCgojIE9mZnNldHMKeF9vIDwtIC1tYXgoc25vd3NjYXBlX3NhbXBsZWQkeCkvMiAjIEV4cGVyaW1lbnQgd2l0aCB0aGUgc2lnbiBhbmQgdGhlIGZyYWN0aW9uCnlfbyA8LSAwICNtYXgoc25vd3NjYXBlX3NhbXBsZWQkeSkKCmRmIDwtIHNub3dzY2FwZV9zYW1wbGVkICU+JQogIG11dGF0ZShhbmdsZSA9IDEgKiBwaS82ICogKDEgLSh4IC0geF9vKS8obl9tYXggLSBuX21pbikpLAogICAgICAgICB4ZW5kID0gKHggKyBsICogY29zKGFuZ2xlKSksCiAgICAgICAgIHllbmQgPSAoeSArIGwgKiBzaW4oYW5nbGUpKSkKCmRmICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geGVuZCwKICAgICAgICAgICAgICAgICAgIHllbmQgPSB5ZW5kLAogICAgICAgICAgICAgICAgICAgc2l6ZSA9IHZhbHVlLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvcikpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygxLCA4KSkgKyAjIEV4cGVyaW1lbnQgd2l0aCB0aGUgcmFuZ2UKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV95X3JldmVyc2UoKSArCiAgY29vcmRfZXF1YWwoZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdnc2F2ZSgic25vd3NjYXBlLWZsb3ctZmllbGQucG5nIikKYGBgCgojIyBQcm9jZXNzIGltYWdlIC0gTWVnaGFuCgpSZWFkIHRoZSBpbWFnZSB1c2luZyBgaW1hZ2VyOjpsb2FkLmltYWdlKClgOgpgYGB7cn0KbWVnaGFuIDwtIGxvYWQuaW1hZ2UoIm1lZ2hhbi5qcGciKQpgYGAKCkNoZWNrIHRoZSBzaXplIG9mIHRoZSBpbWFnZTogCmBgYHtyfQpzdW1tYXJ5KG1lZ2hhbikKYGBgCgpDb252ZXJ0IGltYWdlIHRvIGRhdGEgZnJhbWUgKHN0aWxsIHdvcmtpbmcgd2l0aCB7aW1hZ2VyfSk6CmBgYHtyfQptZWdoYW5fZGYgPC0gbWVnaGFuICU+JQogIGFzLmRhdGEuZnJhbWUod2lkZT0iYyIpICU+JSAKICBtdXRhdGUoY29sb3IgPSByZ2IoYy4xLAogICAgICAgICAgICAgICAgICAgICBjLjIsCiAgICAgICAgICAgICAgICAgICAgIGMuMykpCmBgYAoKQ29udmVydCB0byBncmF5c2NhbGUgYW5kIHRvIGRhdGEgZnJhbWU6CmBgYHtyfQptZWdoYW5fZGZfZ3JheXNjYWxlIDwtIG1lZ2hhbiAlPiUKICBncmF5c2NhbGUoKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKSm9pbiBkYXRhIGZyYW1lcyB3aXRoIGNvbG9yIGFuZCBncmF5c2NhbGUgdmFsdWVzOgpgYGB7cn0KbWVnaGFuX2RmIDwtIG1lZ2hhbl9kZiAlPiUKICBsZWZ0X2pvaW4obWVnaGFuX2RmX2dyYXlzY2FsZSwKICAgICAgICAgICAgYnkgPSBjKCJ4IiwgInkiKSkKYGBgCgpDcm9wIGltYWdlOgpgYGB7cn0KbWVnaGFuX2RmIDwtIG1lZ2hhbl9kZiAlPiUKICBmaWx0ZXIoeCA8PSAxMjUwLCB5IDw9IDEyNTApCmBgYAoKUmV0cmlldmUgZXZlcnkgbnRoIHJvdy9jb2x1bW4gZnJvbSBpbWFnZToKYGBge3J9Cm54IDwtIDIwCm55IDwtIDIwCgptZWdoYW5fc2FtcGxlZCA8LSBtZWdoYW5fZGYgJT4lCiAgZmlsdGVyKHggJSUgbnggPT0gMSwKICAgICAgICAgeSAlJSBueSA9PSAxKSAKYGBgCgpQbG90OgpgYGB7cn0KbWVnaGFuX3NhbXBsZWQgJT4lCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KGFlcyh4LCAKICAgICAgICAgICAgICAgICB5LAogICAgICAgICAgICAgICAgIGNvbG9yID0gdmFsdWUpLAogICAgICAgICAgICAgc2hhcGUgPSAxNSwKICAgICAgICAgICAgIHNpemUgPSA2KSArCiAgc2NhbGVfY29sb3JfZmVybWVudGVyKHBhbGV0dGUgPSAiZ3JlZW5zIikgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGFlcyh4LCAKICAgICAgICAgICAgICAgICB5LCAKICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yLAogICAgICAgICAgICAgICAgIHNpemUgPSB2YWx1ZSkpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKyAKICBzY2FsZV9zaXplKHJhbmdlID0gYygwLjEsIDMpKSArCiAgY29vcmRfZXF1YWwoZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdnc2F2ZSgibWVnaGFuLWNpcmNsZXMucG5nIiwKICAgICAgIGhlaWdodCA9IDgsCiAgICAgICB3aWR0aCA9IDgsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKUGxvdCB3aXRoIGRpZmZlcmVudCBzaGFwZToKYGBge3J9Cm1lZ2hhbl9zYW1wbGVkICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9wb2ludChhZXMoeCwKICAgICAgICAgICAgICAgICB5KSwKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgIHNoYXBlID0gMTUsCiAgICAgICAgICAgICBzaXplID0gNikgKwogIGdlb21fcG9pbnQoYWVzKHgsIAogICAgICAgICAgICAgICAgIHksCiAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvciwKICAgICAgICAgICAgICAgICBzaXplID0gdmFsdWUpLAogICAgICAgICAgICAgc2hhcGUgPSAxNykgKwogICMgZ2VvbV9wb2ludChhZXMoeCwgCiAgIyAgICAgICAgICAgIHkpLAogICMgICAgICAgICAgICBzaXplID0gMC4xLAogICMgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgIyAgICAgICAgICAgIHNoYXBlID0gMjApICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygxLCA0KSkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsgCiAgY29vcmRfZXF1YWwoZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdnc2F2ZSgibWVnaGFuLWRpYW1vbmRzLnBuZyIsCiAgICAgICBoZWlnaHQgPSA4LAogICAgICAgd2lkdGggPSA4LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKClJldHJpZXZlIGV2ZXJ5IG50aCByb3cvY29sdW1uIGZyb20gaW1hZ2U6CmBgYHtyfQpueCA8LSAyNQpueSA8LSAyNQoKbWVnaGFuX3NhbXBsZWQgPC0gbWVnaGFuX2RmICU+JQogIGZpbHRlcih4ICUlIG54ID09IDEsCiAgICAgICAgIHkgJSUgbnkgPT0gMSkgCmBgYAoKRGVmaW5lIGxlbmd0aCBvZiBzZWdtZW50czoKYGBge3J9CmwgPC0gMjUgIyBFeHBlcmltZW50IHdpdGggdGhlIGxlbmd0aApgYGAKCkNyZWF0ZSBhbmQgcGxvdCB0aGUgZmxvdyBmaWVsZDoKYGBge3J9Cm5fbWluIDwtIG1pbihtZWdoYW5fc2FtcGxlZCR4KQpuX21heCA8LSBtYXgobWVnaGFuX3NhbXBsZWQkeCkKCiMgT2Zmc2V0cwp4X28gPC0gMCAjbWF4KG1lZ2hhbl9zYW1wbGVkJHgpLzIgIyBFeHBlcmltZW50IHdpdGggdGhlIHNpZ24gYW5kIHRoZSBmcmFjdGlvbgp5X28gPC0gMCAjbWF4KG1lZ2hhbl9zYW1wbGVkJHkpCiMgSW5pdGlhbCByb3RhdGlvbgppbml0X3IgPC0gMS84CgoKZGYgPC0gbWVnaGFuX3NhbXBsZWQgJT4lCiAgbXV0YXRlKGFuZ2xlID0gKCh4IC0geF9vKSAqICh5IC0geV9vKSkgLyhuX21heCAtIG5fbWluKV4yICogMiAqIHBpICsgaW5pdF9yICogcGksCiAgICAgICAgIHhlbmQgPSAoeCArIGwgKiBjb3MoYW5nbGUpKSwKICAgICAgICAgeWVuZCA9ICh5ICsgbCAqIHNpbihhbmdsZSkpKQoKZGYgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHgsCiAgICAgICAgICAgICAgICAgeSwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSB2YWx1ZSksCiAgICAgICAgICAgICAjY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgc2hhcGUgPSAxNSwKICAgICAgICAgICAgIHNpemUgPSA2KSArCiAgc2NhbGVfY29sb3JfZmVybWVudGVyKHBhbGV0dGUgPSAiR3JleXMiKSArCiAgbmV3X3NjYWxlX2NvbG9yKCkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgsCiAgICAgICAgICAgICAgICAgICB5ID0geSwKICAgICAgICAgICAgICAgICAgIHhlbmQgPSB4ZW5kLAogICAgICAgICAgICAgICAgICAgeWVuZCA9IHllbmQsCiAgICAgICAgICAgICAgICAgICBzaXplID0gdmFsdWUsCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yKSkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDAuNSwgMykpICsgIyBFeHBlcmltZW50IHdpdGggdGhlIHJhbmdlCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIGNvb3JkX2VxdWFsKGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpnZ3NhdmUoIm1lZ2hhbi1mbG93LWZpZWxkLnBuZyIsCiAgICAgICBoZWlnaHQgPSA4LAogICAgICAgd2lkdGggPSA4LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFByb2Nlc3MgaW1hZ2UgLSBNYXJpYW5hCgpSZWFkIHRoZSBpbWFnZSB1c2luZyBgaW1hZ2VyOjpsb2FkLmltYWdlKClgOgpgYGB7cn0KbWFyaWFuYSA8LSBsb2FkLmltYWdlKCJtYXJpYW5hLWdhdG8uanBnIikKYGBgCgpDaGVjayB0aGUgc2l6ZSBvZiB0aGUgaW1hZ2U6IApgYGB7cn0Kc3VtbWFyeShtYXJpYW5hKQpgYGAKCkNvbnZlcnQgaW1hZ2UgdG8gZGF0YSBmcmFtZSAoc3RpbGwgd29ya2luZyB3aXRoIHtpbWFnZXJ9KToKYGBge3J9Cm1hcmlhbmFfZGYgPC0gbWFyaWFuYSAlPiUKICBhcy5kYXRhLmZyYW1lKHdpZGU9ImMiKSAlPiUgCiAgbXV0YXRlKGNvbG9yID0gcmdiKGMuMSwKICAgICAgICAgICAgICAgICAgICAgYy4yLAogICAgICAgICAgICAgICAgICAgICBjLjMpKQpgYGAKCkNvbnZlcnQgdG8gZ3JheXNjYWxlIGFuZCB0byBkYXRhIGZyYW1lOgpgYGB7cn0KbWFyaWFuYV9kZl9ncmF5c2NhbGUgPC0gbWFyaWFuYSAlPiUKICBncmF5c2NhbGUoKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKSm9pbiBkYXRhIGZyYW1lcyB3aXRoIGNvbG9yIGFuZCBncmF5c2NhbGUgdmFsdWVzOgpgYGB7cn0KbWFyaWFuYV9kZiA8LSBtYXJpYW5hX2RmICU+JQogIGxlZnRfam9pbihtYXJpYW5hX2RmX2dyYXlzY2FsZSwKICAgICAgICAgICAgYnkgPSBjKCJ4IiwgInkiKSkKYGBgCgpDcm9wIGltYWdlOgpgYGB7cn0KI21hcmlhbmFfZGYgPC0gbWFyaWFuYV9kZiAlPiUKIyAgZmlsdGVyKHggPD0gMTI1MCwgeSA8PSAxMjUwKQpgYGAKClJldHJpZXZlIGV2ZXJ5IG50aCByb3cvY29sdW1uIGZyb20gaW1hZ2U6CmBgYHtyfQpueCA8LSAzMApueSA8LSAzMAoKbWFyaWFuYV9zYW1wbGVkIDwtIG1hcmlhbmFfZGYgJT4lCiAgZmlsdGVyKHggJSUgbnggPT0gMSwKICAgICAgICAgeSAlJSBueSA9PSAxKSAKYGBgCgpQbG90OgpgYGB7cn0KbWFyaWFuYV9zYW1wbGVkICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9wb2ludChhZXMoeCwgCiAgICAgICAgICAgICAgICAgeSwKICAgICAgICAgICAgICAgICBjb2xvciA9IHZhbHVlKSwKICAgICAgICAgICAgIHNoYXBlID0gMTUsCiAgICAgICAgICAgICBzaXplID0gNikgKwogIHNjYWxlX2NvbG9yX2Zlcm1lbnRlcihwYWxldHRlID0gIkdyZXlzIikgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGFlcyh4LCAKICAgICAgICAgICAgICAgICB5LCAKICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yLAogICAgICAgICAgICAgICAgIHNpemUgPSB2YWx1ZSkpICsgCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKyAKICBzY2FsZV9zaXplKHJhbmdlID0gYygwLjEsIDMpKSArCiAgY29vcmRfZXF1YWwoZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgZ2dzYXZlKCJtZWdoYW4tY2lyY2xlcy5wbmciLAojICAgICAgICBoZWlnaHQgPSA4LAojICAgICAgICB3aWR0aCA9IDgsCiMgICAgICAgIHVuaXRzID0gImluIikKYGBgCgpQbG90IHdpdGggZGlmZmVyZW50IHNoYXBlOgpgYGB7cn0KbWFyaWFuYV9zYW1wbGVkICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9wb2ludChhZXMoeCwKICAgICAgICAgICAgICAgICB5KSwKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgIHNoYXBlID0gMTUsCiAgICAgICAgICAgICBzaXplID0gNikgKwogIGdlb21fcG9pbnQoYWVzKHgsIAogICAgICAgICAgICAgICAgIHksCiAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvciwKICAgICAgICAgICAgICAgICBzaXplID0gdmFsdWUpLAogICAgICAgICAgICAgc2hhcGUgPSAxNykgKwogICMgZ2VvbV9wb2ludChhZXMoeCwgCiAgIyAgICAgICAgICAgIHkpLAogICMgICAgICAgICAgICBzaXplID0gMC4xLAogICMgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgIyAgICAgICAgICAgIHNoYXBlID0gMjApICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygxLCA0KSkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsgCiAgY29vcmRfZXF1YWwoZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMgZ2dzYXZlKCJtZWdoYW4tZGlhbW9uZHMucG5nIiwKIyAgICAgICAgaGVpZ2h0ID0gOCwKIyAgICAgICAgd2lkdGggPSA4LAojICAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKUmV0cmlldmUgZXZlcnkgbnRoIHJvdy9jb2x1bW4gZnJvbSBpbWFnZToKYGBge3J9Cm54IDwtIDI1Cm55IDwtIDI1CgptYXJpYW5hX3NhbXBsZWQgPC0gbWFyaWFuYV9kZiAlPiUKICBmaWx0ZXIoeCAlJSBueCA9PSAxLAogICAgICAgICB5ICUlIG55ID09IDEpIApgYGAKCkRlZmluZSBsZW5ndGggb2Ygc2VnbWVudHM6CmBgYHtyfQpsIDwtIDI1ICMgRXhwZXJpbWVudCB3aXRoIHRoZSBsZW5ndGgKYGBgCgpDcmVhdGUgYW5kIHBsb3QgdGhlIGZsb3cgZmllbGQ6CmBgYHtyfQpuX21pbiA8LSBtaW4obWVnaGFuX3NhbXBsZWQkeCkKbl9tYXggPC0gbWF4KG1lZ2hhbl9zYW1wbGVkJHgpCgojIE9mZnNldHMKeF9vIDwtIDAgI21heChtZWdoYW5fc2FtcGxlZCR4KS8yICMgRXhwZXJpbWVudCB3aXRoIHRoZSBzaWduIGFuZCB0aGUgZnJhY3Rpb24KeV9vIDwtIDAgI21heChtZWdoYW5fc2FtcGxlZCR5KQojIEluaXRpYWwgcm90YXRpb24KaW5pdF9yIDwtIDEvOAoKCmRmIDwtIG1hcmlhbmFfc2FtcGxlZCAlPiUKICBtdXRhdGUoYW5nbGUgPSAoKHggLSB4X28pICogKHkgLSB5X28pKSAvKG5fbWF4IC0gbl9taW4pXjIgKiAyICogcGkgKyBpbml0X3IgKiBwaSwKICAgICAgICAgeGVuZCA9ICh4ICsgbCAqIGNvcyhhbmdsZSkpLAogICAgICAgICB5ZW5kID0gKHkgKyBsICogc2luKGFuZ2xlKSkpCgpkZiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCwKICAgICAgICAgICAgICAgICB5LCAKICAgICAgICAgICAgICAgICBjb2xvciA9IHZhbHVlKSwKICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgIHNoYXBlID0gMTUsCiAgICAgICAgICAgICBzaXplID0gNikgKwogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcihwYWxldHRlID0gIkdyZXlzIikgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geGVuZCwKICAgICAgICAgICAgICAgICAgIHllbmQgPSB5ZW5kLAogICAgICAgICAgICAgICAgICAgc2l6ZSA9IHZhbHVlLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvcikpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygwLjUsIDMpKSArICMgRXhwZXJpbWVudCB3aXRoIHRoZSByYW5nZQogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBjb29yZF9lcXVhbChleHBhbmQgPSBGQUxTRSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKZ2dzYXZlKCJtYXJpYW5hLWZsb3ctZmllbGQucG5nIiwKICAgICAgIGhlaWdodCA9IDgsCiAgICAgICB3aWR0aCA9IDgsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKIyMgUHJvY2VzcyBpbWFnZSAtIE9yZWdvbgoKUmVhZCB0aGUgaW1hZ2UgdXNpbmcgYGltYWdlcjo6bG9hZC5pbWFnZSgpYDoKYGBge3J9Cm9yZWdvbiA8LSBsb2FkLmltYWdlKCJvcmVnb24uanBnIikKYGBgCgpDaGVjayB0aGUgc2l6ZSBvZiB0aGUgaW1hZ2U6IApgYGB7cn0Kc3VtbWFyeShvcmVnb24pCmBgYAoKQ29udmVydCBpbWFnZSB0byBkYXRhIGZyYW1lIChzdGlsbCB3b3JraW5nIHdpdGgge2ltYWdlcn0pOgpgYGB7cn0Kb3JlZ29uX2RmIDwtIG9yZWdvbiAlPiUKICBhcy5kYXRhLmZyYW1lKHdpZGU9ImMiKSAlPiUgCiAgbXV0YXRlKGNvbG9yID0gcmdiKGMuMSwKICAgICAgICAgICAgICAgICAgICAgYy4yLAogICAgICAgICAgICAgICAgICAgICBjLjMpKQpgYGAKCkNvbnZlcnQgdG8gZ3JheXNjYWxlIGFuZCB0byBkYXRhIGZyYW1lOgpgYGB7cn0Kb3JlZ29uX2RmX2dyYXlzY2FsZSA8LSBvcmVnb24gJT4lCiAgZ3JheXNjYWxlKCkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCkpvaW4gZGF0YSBmcmFtZXMgd2l0aCBjb2xvciBhbmQgZ3JheXNjYWxlIHZhbHVlczoKYGBge3J9Cm9yZWdvbl9kZiA8LSBvcmVnb25fZGYgJT4lCiAgbGVmdF9qb2luKG9yZWdvbl9kZl9ncmF5c2NhbGUsCiAgICAgICAgICAgIGJ5ID0gYygieCIsICJ5IikpCmBgYAoKUmV0cmlldmUgZXZlcnkgbnRoIHJvdy9jb2x1bW4gZnJvbSBpbWFnZToKYGBge3J9Cm54IDwtIDEwCm55IDwtIDEwCgpvcmVnb25fc2FtcGxlZCA8LSBvcmVnb25fZGYgJT4lCiAgZmlsdGVyKHggJSUgbnggPT0gMSwKICAgICAgICAgeSAlJSBueSA9PSAxKSAKYGBgCgpDcmVhdGUgYW5kIHBsb3QgdGhlIGZsb3cgZmllbGQ6CmBgYHtyfQojIE9mZnNldHMKeF9vIDwtIDU2MAp5X28gPC0gMTQwCgoKZGYgPC0gb3JlZ29uX3NhbXBsZWQgJT4lCiAgI2xlbmd0aCBvZiBzZWdtZW50cyBkZXBlbmRzIG9uIHBvc2l0aW9uLCBhbmQgdGhlIGFuZ2xlIGlzIGNvbnN0YW50IGZyb20geF9vLCB5X28KICBtdXRhdGUoeGVuZCA9ICh4ICsgKHggLSB4X28pLzMwICogcGkvNCksCiAgICAgICAgIHllbmQgPSAoeSArICh5IC0geV9vKS8zMCAqIHBpLzQpKQoKZGYgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHgsCiAgICAgICAgICAgICAgICAgeSwKICAgICAgICAgICAgICAgICBjb2xvciA9IHZhbHVlKSwKICAgICAgICAgICAgIHNoYXBlID0gMTUsCiAgICAgICAgICAgICBzaXplID0gNikgKwogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcihwYWxldHRlID0gIkdyZXlzIikgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geGVuZCwKICAgICAgICAgICAgICAgICAgIHllbmQgPSB5ZW5kLAogICAgICAgICAgICAgICAgICAgc2l6ZSA9IHZhbHVlLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvcikpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygwLjEsIDIpKSArICMgRXhwZXJpbWVudCB3aXRoIHRoZSByYW5nZQogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBjb29yZF9lcXVhbChleHBhbmQgPSBGQUxTRSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKZ2dzYXZlKCJvcmVnb24tZmxvdy1maWVsZC5wbmciLAogICAgICAgaGVpZ2h0ID0gOCwKICAgICAgIHdpZHRoID0gOCwKICAgICAgIHVuaXRzID0gImluIikKYGBgCgojIyBXYWxkcG9ydAoKU2VlIGh0dHBzOi8vdHdpdHRlci5jb20vbWFya19sYXdsZXIvc3RhdHVzLzE0OTA0Njc2NDE2NzYzMzcxNTQvcGhvdG8vMiBmb3IgdGhpcyBpbWFnZToKIVtdKFdhbGRwb3J0LmpwZykKClJlYWQgdGhlIGltYWdlIHVzaW5nIGBpbWFnZXI6OmxvYWQuaW1hZ2UoKWA6CmBgYHtyfQp3YWxkcG9ydCA8LSBsb2FkLmltYWdlKCJ3YWxkcG9ydC5qcGciKQpgYGAKCkNoZWNrIHRoZSBzaXplIG9mIHRoZSBpbWFnZTogCmBgYHtyfQpzdW1tYXJ5KHdhbGRwb3J0KQpgYGAKCkNvbnZlcnQgaW1hZ2UgdG8gZGF0YSBmcmFtZSAoc3RpbGwgd29ya2luZyB3aXRoIHtpbWFnZXJ9KToKYGBge3J9CndhbGRwb3J0X2RmIDwtIHdhbGRwb3J0ICU+JQogIGFzLmRhdGEuZnJhbWUod2lkZT0iYyIpICU+JSAKICBtdXRhdGUoY29sb3IgPSByZ2IoYy4xLAogICAgICAgICAgICAgICAgICAgICBjLjIsCiAgICAgICAgICAgICAgICAgICAgIGMuMykpCmBgYAoKQ29udmVydCB0byBncmF5c2NhbGUgYW5kIHRvIGRhdGEgZnJhbWU6CmBgYHtyfQp3YWxkcG9ydF9kZl9ncmF5c2NhbGUgPC0gd2FsZHBvcnQgJT4lCiAgZ3JheXNjYWxlKCkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCkpvaW4gZGF0YSBmcmFtZXMgd2l0aCBjb2xvciBhbmQgZ3JheXNjYWxlIHZhbHVlczoKYGBge3J9CndhbGRwb3J0X2RmIDwtIHdhbGRwb3J0X2RmICU+JQogIGxlZnRfam9pbih3YWxkcG9ydF9kZl9ncmF5c2NhbGUsCiAgICAgICAgICAgIGJ5ID0gYygieCIsICJ5IikpCmBgYAoKUmV0cmlldmUgZXZlcnkgbnRoIHJvdy9jb2x1bW4gZnJvbSBpbWFnZToKYGBge3J9Cm54IDwtIDIwCm55IDwtIDIwCgp3YWxkcG9ydF9zYW1wbGVkIDwtIHdhbGRwb3J0X2RmICU+JQogIGZpbHRlcih4ICUlIG54ID09IDEsCiAgICAgICAgIHkgJSUgbnkgPT0gMSkgCmBgYAoKQ3JlYXRlIGFuZCBwbG90IHRoZSBmbG93IGZpZWxkOgpgYGB7cn0KIyBPZmZzZXRzCnhfbyA8LSAtMTAwMAp5X28gPC0gNzcwCgoKZGYgPC0gd2FsZHBvcnRfc2FtcGxlZCAlPiUKICAjbGVuZ3RoIG9mIHNlZ21lbnRzIGRlcGVuZHMgb24gcG9zaXRpb24sIGFuZCB0aGUgYW5nbGUgaXMgY29uc3RhbnQgZnJvbSB4X28sIHlfbwogIG11dGF0ZSh4ZW5kID0gKHggKyAoeCAtIHhfbykvMjAgKiBwaS84KSwKICAgICAgICAgeWVuZCA9ICh5ICsgKHkgLSB5X28pLzIwICogcGkvOCkpCgpkZiAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCwKICAgICAgICAgICAgICAgICB5LAogICAgICAgICAgICAgICAgIGNvbG9yID0gcmV2KHkpKSwKICAgICAgICAgICAgIHNoYXBlID0gMTUsCiAgICAgICAgICAgICBzaXplID0gNikgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBNZXhCcmV3ZXI6Om1leC5icmV3ZXIoIkF0ZW50YWRvIikpICsKICBuZXdfc2NhbGVfY29sb3IoKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwKICAgICAgICAgICAgICAgICAgIHkgPSB5LAogICAgICAgICAgICAgICAgICAgeGVuZCA9IHhlbmQsCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geWVuZCwKICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YWx1ZSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3IpKSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMC4wMSwgMC43NSkpICsgIyBFeHBlcmltZW50IHdpdGggdGhlIHJhbmdlCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIGNvb3JkX2VxdWFsKGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpnZ3NhdmUoIndhbGRwb3J0LWZsb3ctZmllbGQucG5nIiwKICAgICAgIGhlaWdodCA9IDgsCiAgICAgICB3aWR0aCA9IDgsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKIyMgT25lIG1vcmUgb2YgYW5vdGhlciBiZWFjaCEgQ2hlY2sgdGhpczogaHR0cHM6Ly90d2l0dGVyLmNvbS9HaXVJaWFub0RNZWRpY2kvc3RhdHVzLzE0OTA3MTUxNDAwNTE5NDc1MjEKCiFbXShiZWFjaC5qcGcpCgpSZWFkIHRoZSBpbWFnZSB1c2luZyBgaW1hZ2VyOjpsb2FkLmltYWdlKClgOgpgYGB7cn0KYmVhY2ggPC0gbG9hZC5pbWFnZSgiYmVhY2guanBnIikKYGBgCgpDaGVjayB0aGUgc2l6ZSBvZiB0aGUgaW1hZ2U6IApgYGB7cn0KYmVhY2gKYGBgCgpDb252ZXJ0IGltYWdlIHRvIGRhdGEgZnJhbWUgKHN0aWxsIHdvcmtpbmcgd2l0aCB7aW1hZ2VyfSk6CmBgYHtyfQpiZWFjaF9kZiA8LSBiZWFjaCAlPiUKICBhcy5kYXRhLmZyYW1lKHdpZGU9ImMiKSAlPiUgCiAgbXV0YXRlKGNvbG9yID0gcmdiKGMuMSwKICAgICAgICAgICAgICAgICAgICAgYy4yLAogICAgICAgICAgICAgICAgICAgICBjLjMpKQpgYGAKCkNvbnZlcnQgdG8gZ3JheXNjYWxlIGFuZCB0byBkYXRhIGZyYW1lOgpgYGB7cn0KYmVhY2hfZGZfZ3JheXNjYWxlIDwtIGJlYWNoICU+JQogIGdyYXlzY2FsZSgpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKYGBgCgpKb2luIGRhdGEgZnJhbWVzIHdpdGggY29sb3IgYW5kIGdyYXlzY2FsZSB2YWx1ZXM6CmBgYHtyfQpiZWFjaF9kZiA8LSBiZWFjaF9kZiAlPiUKICBsZWZ0X2pvaW4oYmVhY2hfZGZfZ3JheXNjYWxlLAogICAgICAgICAgICBieSA9IGMoIngiLCAieSIpKQpgYGAKClJldHJpZXZlIGV2ZXJ5IG50aCByb3cvY29sdW1uIGZyb20gaW1hZ2U6CmBgYHtyfQpueCA8LSAxMApueSA8LSAxMAoKYmVhY2hfc2FtcGxlZCA8LSBiZWFjaF9kZiAlPiUKICBmaWx0ZXIoeCAlJSBueCA9PSAxLAogICAgICAgICB5ICUlIG55ID09IDEpIApgYGAKCkNyZWF0ZSBhbmQgcGxvdCB0aGUgZmxvdyBmaWVsZDoKYGBge3J9CiMgT2Zmc2V0cwp4X28gPC0gMjAwCnlfbyA8LSA0ODAKCgpkZiA8LSBiZWFjaF9zYW1wbGVkICU+JQogICNsZW5ndGggb2Ygc2VnbWVudHMgZGVwZW5kcyBvbiBwb3NpdGlvbiwgYW5kIHRoZSBhbmdsZSBpcyBjb25zdGFudCBmcm9tIHhfbywgeV9vCiAgbXV0YXRlKHhlbmQgPSAoeCArICh4IC0geF9vKS8yMCAqIHBpLzQpLAogICAgICAgICB5ZW5kID0gKHkgKyAoeSAtIHlfbykvMjAgKiBwaS80KSkKCmRmICU+JQogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4LAogICAgICAgICAgICAgICAgIHksCiAgICAgICAgICAgICAgICAgY29sb3IgPSByZXYoeSkpLAogICAgICAgICAgICAgc2hhcGUgPSAxNSwKICAgICAgICAgICAgIHNpemUgPSA2KSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IE1leEJyZXdlcjo6bWV4LmJyZXdlcigiQXRlbnRhZG8iKSkgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4LAogICAgICAgICAgICAgICAgICAgeSA9IHksCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geGVuZCwKICAgICAgICAgICAgICAgICAgIHllbmQgPSB5ZW5kLAogICAgICAgICAgICAgICAgICAgc2l6ZSA9IHNxcnQoKHggLSB4X28pXjIgKyAoeSAtIHlfbyleMiksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IGNvbG9yKSkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDAuNSwgMS41KSkgKyAjIEV4cGVyaW1lbnQgd2l0aCB0aGUgcmFuZ2UKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICBzY2FsZV95X3JldmVyc2UoKSArCiAgY29vcmRfZXF1YWwoZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdnc2F2ZSgiYmVhY2gtZmxvdy1maWVsZC5wbmciLAogICAgICAgaGVpZ2h0ID0gOCwKICAgICAgIHdpZHRoID0gOCwKICAgICAgIHVuaXRzID0gImluIikKYGBgCgojIyBGbG93ZXIgMSBhbmQgc3BpcmFscwoKIVtdKGZsb3dlci5qcGcpCgpSZWFkIHRoZSBpbWFnZSB1c2luZyBgaW1hZ2VyOjpsb2FkLmltYWdlKClgOgpgYGB7cn0KZmxvd2VyIDwtIGxvYWQuaW1hZ2UoImZsb3dlci5qcGciKQpgYGAKCkNoZWNrIHRoZSBzaXplIG9mIHRoZSBpbWFnZTogCmBgYHtyfQpmbG93ZXIKYGBgCgpDb252ZXJ0IGltYWdlIHRvIGRhdGEgZnJhbWUgKHN0aWxsIHdvcmtpbmcgd2l0aCB7aW1hZ2VyfSk6CmBgYHtyfQpmbG93ZXJfZGYgPC0gZmxvd2VyICU+JQogIGFzLmRhdGEuZnJhbWUod2lkZT0iYyIpICU+JSAKICBtdXRhdGUoY29sb3IgPSByZ2IoYy4xLAogICAgICAgICAgICAgICAgICAgICBjLjIsCiAgICAgICAgICAgICAgICAgICAgIGMuMykpCmBgYAoKQ29udmVydCB0byBncmF5c2NhbGUgYW5kIHRvIGRhdGEgZnJhbWU6CmBgYHtyfQpmbG93ZXJfZGZfZ3JheXNjYWxlIDwtIGZsb3dlciAlPiUKICBncmF5c2NhbGUoKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCmBgYAoKSm9pbiBkYXRhIGZyYW1lcyB3aXRoIGNvbG9yIGFuZCBncmF5c2NhbGUgdmFsdWVzOgpgYGB7cn0KZmxvd2VyX2RmIDwtIGZsb3dlcl9kZiAlPiUKICBsZWZ0X2pvaW4oZmxvd2VyX2RmX2dyYXlzY2FsZSwKICAgICAgICAgICAgYnkgPSBjKCJ4IiwgInkiKSkKYGBgCgpDb252ZXJ0IHRoZSBpbWFnZSB0byBzZiB0byB1c2Ugc2YgZnVuY3Rpb25zIHRvIGZpbmQgbmVhcmVzdCBmZWF0dXJlcyB0byBib3Jyb3cgY29sb3JzOgpgYGB7cn0KZmxvd2VyX3NmIDwtIGZsb3dlcl9kZiAlPiUKICAgIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpKQoKYGBgCgpHb2xkZW4gc3BpcmFsOgpgYGB7cn0KI3BvaW50cyA8LSA1MDAKCiMgRGVmaW5pbmcgdGhlIEdvbGRlbiBBbmdsZQphbmdsZSA8LSBwaSAqICgzIC0gc3FydCg1KSkKCiN0X28gPC0gc2VxKDEsIDEwLCAxKSAKCmdzIDwtIGRhdGEuZnJhbWUodF9vID0gc2VxKDEsIDc1MCwgMC4xKSAqIGFuZ2xlKSAlPiUKICBtdXRhdGUoeF9vID0gMzAwICsgdF9vICogc2luKHRfbyksCiAgICAgICAgIHlfbyA9IDMwMCArIHRfbyAqIGNvcyh0X28pLAogICAgICAgICB4X2VuZCA9IHhfbyArIDIwICogc2luKHRfbyksCiAgICAgICAgIHlfZW5kID0geV9vICsgMjAgKiBjb3ModF9vKSkKYGBgCgpDb252ZXJ0IHRvIHNmIHRvIGJvcnJvdyB0aGUgY29sb3JzIGZyb20gdGhlIGltYWdlOgpgYGB7cn0KZ3MgPC0gZ3MgJT4lCiAgbXV0YXRlKHggPSB4X28sCiAgICAgICAgIHkgPSB5X28pICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpKQpgYGAKCgpGaW5kIHRoZSBuZWFyZXN0IGZlYXR1cmUgaW4gdGhlIG9yaWdpbmFsIGltYWdlIHRvIGJvcnJvdyB0aGUgY29sb3JzOgpgYGB7cn0KZmxvd2VyX2NvbG9ycyA8LSBmbG93ZXJfZGZbZ3MgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RfbmVhcmVzdF9mZWF0dXJlKGZsb3dlcl9zZiksXQpgYGAKCkJpbmQgdGhlIGNvbG9ycyB0byB0aGUgcGFja2VkIGNpcmNsZXM6CmBgYHtyfQpncyA8LSBncyAlPiUKICBtdXRhdGUoY29sb3IgPSBmbG93ZXJfY29sb3JzJGNvbG9yLAogICAgICAgICB2YWx1ZSA9IGZsb3dlcl9jb2xvcnMkdmFsdWUpCmBgYAoKUGxvdDoKYGBge3J9CiMgTWFrZSBhIHNjYXR0ZXIgcGxvdCBvZiBwb2ludHMgaW4gYSBzcGlyYWwKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YT0gZmxvd2VyX2RmLAogICAgICAgICAgICAgYWVzKHgsCiAgICAgICAgICAgICAgICAgeSwKICAgICAgICAgICAgICAgICBjb2xvciA9IHZhbHVlKSwKICAgICAgICAgICAgICNjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICBzaGFwZSA9IDE1LAogICAgICAgICAgICAgc2l6ZSA9IDYpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gTWV4QnJld2VyOjptZXguYnJld2VyKCJBdGVudGFkbyIpKSArCiAgI3NjYWxlX2NvbG9yX2Rpc3RpbGxlcihwYWxldHRlID0gIkJsdWVzIikgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IGdzICU+JQogICAgICAgICAgICAgICAgIGZpbHRlcih4X28gPiAxMCAmIHhfbyA8IDcxMCwKICAgICAgICAgICAgICAgICAgICAgICAgeV9vID4gMTAgJiB5X28gPCA5NTApLAogICAgICAgICAgICAgICBhZXMoeCA9IHhfbywKICAgICAgICAgICAgICAgICAgIHhlbmQgPSB4X2VuZCwKICAgICAgICAgICAgICAgICAgIHkgPSB5X28sCiAgICAgICAgICAgICAgICAgICB5ZW5kID0geV9lbmQsCiAgICAgICAgICAgICAgICAgICBzaXplID0gdF9vLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvcikpICsKICBzY2FsZV95X3JldmVyc2UoKSArCiAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMC4xLCAzKSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKZ2dzYXZlKCJmbG93ZXItZmxvdy1maWVsZC5wbmciLAogICAgICAgaGVpZ2h0ID0gOCwKICAgICAgIHdpZHRoID0gOCwKICAgICAgIHVuaXRzID0gImluIikKYGBgCgojIyBGbG93ZXIgMiBhbmQgc3BpcmFscwoKIVtdKGZsb3dlci0yLmpwZykKClJlYWQgdGhlIGltYWdlIHVzaW5nIGBpbWFnZXI6OmxvYWQuaW1hZ2UoKWA6CmBgYHtyfQpmbG93ZXIgPC0gbG9hZC5pbWFnZSgiZmxvd2VyLTIuanBnIikKYGBgCgpDaGVjayB0aGUgc2l6ZSBvZiB0aGUgaW1hZ2U6IApgYGB7cn0KZmxvd2VyCmBgYAoKQ29udmVydCBpbWFnZSB0byBkYXRhIGZyYW1lIChzdGlsbCB3b3JraW5nIHdpdGgge2ltYWdlcn0pOgpgYGB7cn0KZmxvd2VyX2RmIDwtIGZsb3dlciAlPiUKICBhcy5kYXRhLmZyYW1lKHdpZGU9ImMiKSAlPiUgCiAgbXV0YXRlKGNvbG9yID0gcmdiKGMuMSwKICAgICAgICAgICAgICAgICAgICAgYy4yLAogICAgICAgICAgICAgICAgICAgICBjLjMpKQpgYGAKCkNvbnZlcnQgdG8gZ3JheXNjYWxlIGFuZCB0byBkYXRhIGZyYW1lOgpgYGB7cn0KZmxvd2VyX2RmX2dyYXlzY2FsZSA8LSBmbG93ZXIgJT4lCiAgZ3JheXNjYWxlKCkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQpgYGAKCkpvaW4gZGF0YSBmcmFtZXMgd2l0aCBjb2xvciBhbmQgZ3JheXNjYWxlIHZhbHVlczoKYGBge3J9CmZsb3dlcl9kZiA8LSBmbG93ZXJfZGYgJT4lCiAgbGVmdF9qb2luKGZsb3dlcl9kZl9ncmF5c2NhbGUsCiAgICAgICAgICAgIGJ5ID0gYygieCIsICJ5IikpCmBgYAoKQ29udmVydCB0aGUgaW1hZ2UgdG8gc2YgdG8gdXNlIHNmIGZ1bmN0aW9ucyB0byBmaW5kIG5lYXJlc3QgZmVhdHVyZXMgdG8gYm9ycm93IGNvbG9yczoKYGBge3J9CmZsb3dlcl9zZiA8LSBmbG93ZXJfZGYgJT4lCiAgICBzdF9hc19zZihjb29yZHMgPSBjKCJ4IiwgInkiKSkKYGBgCgpHb2xkZW4gc3BpcmFsOgpgYGB7cn0KI3BvaW50cyA8LSA1MDAKCiMgRGVmaW5pbmcgdGhlIEdvbGRlbiBBbmdsZQphbmdsZSA8LSBwaSAqICgzIC0gc3FydCg1KSkKCiN0X28gPC0gc2VxKDEsIDEwLCAxKSAKCmdzIDwtIGRhdGEuZnJhbWUodF9vID0gc2VxKDEsIDc1MCwgMC4xKSAqIGFuZ2xlKSAlPiUKICBtdXRhdGUoeF9vID0gMTYwICsgdF9vICogc2luKHRfbyksCiAgICAgICAgIHlfbyA9IDMxMCArIHRfbyAqIGNvcyh0X28pLAogICAgICAgICB4X2VuZCA9IHhfbyArIDIwICogc2luKHRfbyksCiAgICAgICAgIHlfZW5kID0geV9vICsgMjAgKiBjb3ModF9vKSkKYGBgCgpDb252ZXJ0IHRvIHNmIHRvIGJvcnJvdyB0aGUgY29sb3JzIGZyb20gdGhlIGltYWdlOgpgYGB7cn0KZ3MgPC0gZ3MgJT4lCiAgbXV0YXRlKHggPSB4X28sCiAgICAgICAgIHkgPSB5X28pICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoIngiLCAieSIpKQpgYGAKCkZpbmQgdGhlIG5lYXJlc3QgZmVhdHVyZSBpbiB0aGUgb3JpZ2luYWwgaW1hZ2UgdG8gYm9ycm93IHRoZSBjb2xvcnM6CmBgYHtyfQpmbG93ZXJfY29sb3JzIDwtIGZsb3dlcl9kZltncyAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdF9uZWFyZXN0X2ZlYXR1cmUoZmxvd2VyX3NmKSxdCmBgYAoKQmluZCB0aGUgY29sb3JzIHRvIHRoZSBzcGlyYWw6CmBgYHtyfQpncyA8LSBncyAlPiUKICBtdXRhdGUoY29sb3IgPSBmbG93ZXJfY29sb3JzJGNvbG9yLAogICAgICAgICB2YWx1ZSA9IGZsb3dlcl9jb2xvcnMkdmFsdWUpCmBgYAoKUGxvdDoKYGBge3J9CiMgTWFrZSBhIHNjYXR0ZXIgcGxvdCBvZiBwb2ludHMgaW4gYSBzcGlyYWwKZ2dwbG90KCkgKwogICMgZ2VvbV9wb2ludChkYXRhPSBmbG93ZXJfZGYsCiAgIyAgICAgICAgICAgIGFlcyh4LAogICMgICAgICAgICAgICAgICAgeSwKICAjICAgICAgICAgICAgICAgIGNvbG9yID0gdmFsdWUpLAogICMgICAgICAgICAgICAjY29sb3IgPSAiYmxhY2siLAogICMgICAgICAgICAgICBzaGFwZSA9IDE1LAogICMgICAgICAgICAgICBzaXplID0gNikgKwogICMgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IE1leEJyZXdlcjo6bWV4LmJyZXdlcigiQXRlbnRhZG8iKSkgKwogICMgc2NhbGVfY29sb3JfZGlzdGlsbGVyKHBhbGV0dGUgPSAiQmx1ZXMiKSArCiAgIyBuZXdfc2NhbGVfY29sb3IoKSArCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBncyAlPiUKICAgICAgICAgICAgICAgICBmaWx0ZXIoeF9vID4gMTAgJiB4X28gPCA3MTAsCiAgICAgICAgICAgICAgICAgICAgICAgIHlfbyA+IDEwICYgeV9vIDwgOTUwKSwKICAgICAgICAgICAgICAgYWVzKHggPSB4X28sCiAgICAgICAgICAgICAgICAgICB4ZW5kID0geF9lbmQsCiAgICAgICAgICAgICAgICAgICB5ID0geV9vLAogICAgICAgICAgICAgICAgICAgeWVuZCA9IHlfZW5kLAogICAgICAgICAgICAgICAgICAgc2l6ZSA9IHRfbywKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3IpKSArCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KCkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDAuMSwgMykpICsKICBjb29yZF9lcXVhbCgpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCmdnc2F2ZSgiZmxvd2VyLTItZmxvdy1maWVsZC5wbmciLAogICAgICAgaGVpZ2h0ID0gOCwKICAgICAgIHdpZHRoID0gOCwKICAgICAgIHVuaXRzID0gImluIikKYGBgCgoKCg==